package com.jichangxiu.system.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.jichangxiu.common.annotation.Dynamic;
import com.jichangxiu.common.annotation.Scope;
import com.jichangxiu.common.constant.Constants;
import com.jichangxiu.common.entity.bo.ValidateBos;
import com.jichangxiu.common.enums.ScopeType;
import com.jichangxiu.common.exception.ServiceException;
import com.jichangxiu.common.utils.ConvertUtils;
import com.jichangxiu.framework.service.impl.BaseServiceImpl;
import com.jichangxiu.framework.utils.NoUtils;
import com.jichangxiu.framework.utils.SecurityUtils;
import com.jichangxiu.framework.utils.ValidateUtils;
import com.jichangxiu.system.entity.bo.SysPostBo;
import com.jichangxiu.system.entity.bo.SysPostDeptBo;
import com.jichangxiu.system.entity.dto.add.AddSysPost;
import com.jichangxiu.system.entity.dto.edit.EditSysPost;
import com.jichangxiu.system.entity.dto.query.QuerySysPost;
import com.jichangxiu.system.entity.po.SysPost;
import com.jichangxiu.system.entity.po.SysPostDept;
import com.jichangxiu.system.entity.po.SysUserPost;
import com.jichangxiu.system.entity.vo.SysPostVo;
import com.jichangxiu.system.mapper.SysPostMapper;
import com.jichangxiu.system.service.SysPostDeptService;
import com.jichangxiu.system.service.SysPostService;
import com.jichangxiu.system.service.SysUserPostService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.validation.constraints.NotBlank;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * SysPostServiceImpl:
 *
 * @author JiChangXiu
 * @create 2024-06-28 13:44:31
 */
@Service
public class SysPostServiceImpl extends BaseServiceImpl<SysPostMapper, SysPost, SysPostVo> implements SysPostService {

    @Resource
    private SysUserPostService sysUserPostService;

    @Resource
    private SysPostDeptService sysPostDeptService;

    /**
     * 新增系统岗位
     *
     * @param addSysPost 新增系统岗位视图对象
     * @return 是否成功
     */
    @Dynamic
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean add(AddSysPost addSysPost) {
        SysPost sysPost = addAndEditValidBo(ConvertUtils.convert(addSysPost, SysPostBo.class));
        sysPost.setId(IdWorker.getIdStr());
        boolean b1 = getBaseMapper().insert(sysPost) > 0;
        boolean b2 = true;
        if (ScopeType.CUSTOM.name().equals(sysPost.getScope())) {
            List<SysPostDept> addSysPostDeptList = new ArrayList<>();
            addSysPost.getDeptIdList().forEach(deptId -> addSysPostDeptList.add(SysPostDept.builder().deptId(deptId).postId(sysPost.getId()).tenantId(SecurityUtils.getTenantId()).build()));
            if (ObjectUtil.isNotEmpty(addSysPostDeptList))
                b2 = sysPostDeptService.saveBatch(addSysPostDeptList);
        }
        return b1 && b2;
    }

    /**
     * 单（多）选删除系统岗位
     *
     * @param idList 系统岗位主键列表
     * @return 是否成功
     */
    @Dynamic
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean del(List<String> idList) {
        boolean b1 = getBaseMapper().deleteBatchIds(delValid(idList)) > 0;
        // 跟角色有关的部门关联关系一起删除
        boolean b2 = sysPostDeptService.remove(new LambdaQueryWrapper<SysPostDept>().in(SysPostDept::getPostId, idList));
        return b1 && b2;
    }

    /**
     * 修改系统岗位
     *
     * @param editSysPost 修改系统岗位视图对象
     * @return 是否成功
     */
    @Dynamic
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean edit(EditSysPost editSysPost) {
        boolean b1 = getBaseMapper().updateById(addAndEditValidBo(ConvertUtils.convert(editSysPost, SysPostBo.class))) > 0;
        boolean b2 = true;
        // 如果修改了数据范围，则清空部门列表
        if (!ScopeType.CUSTOM.name().equals(editSysPost.getScope()))
            editSysPost.setDeptIdList(new ArrayList<>());
        else {
            // 数据库中保存的角色部门关联关系
            List<SysPostDept> sysPostDeptList = sysPostDeptService.list(new LambdaQueryWrapper<SysPostDept>().in(SysPostDept::getPostId, editSysPost.getId()));
            if (!sysPostDeptList.stream().map(SysPostDept::getDeptId).collect(Collectors.toList()).equals(editSysPost.getDeptIdList()))
                b2 = sysPostDeptService.addOrDelOrEdit(editSysPost, ConvertUtils.convert(sysPostDeptList, SysPostDeptBo.class));
        }
        return b1 && b2;
    }

    /**
     * 根据主键查询系统岗位
     *
     * @param id       系统岗位主键
     * @param isExtend 是否扩展
     * @return 系统岗位详情
     */
    @Dynamic
    @Override
    public SysPostVo info(@NotBlank(message = "主键不能为空") String id, Boolean isExtend) {
        SysPostVo sysPostVo = getBaseMapper().selectJoinOne(SysPostVo.class, buildQueryWrapper(QuerySysPost.builder().id(id).build()));
        if (Boolean.TRUE.equals(isExtend) && ObjectUtil.isNotEmpty(sysPostVo))
            sysPostVo = renderSysPostVoList(ListUtil.toList(sysPostVo)).get(0);
        return sysPostVo;
    }

    /**
     * （可分页）查询系统岗位列表
     *
     * @param querySysPost 查询视图对象
     * @return （分页）系统岗位列表
     */
    @Dynamic
    @Override
    public Page<SysPostVo> lists(QuerySysPost querySysPost) {
        Page<SysPostVo> sysPostVoPage = getBaseMapper().selectJoinPage(startPage(), SysPostVo.class, buildQueryWrapper(querySysPost));
        if (Boolean.TRUE.equals(querySysPost.getIsExtend()))
            sysPostVoPage.setRecords(renderSysPostVoList(sysPostVoPage.getRecords()));
        return sysPostVoPage;
    }

    /**
     * 新增 / 修改 前校验
     *
     * @param sysPostBo 校验用业务对象
     * @return 数据库操作对象
     */
    private SysPost addAndEditValidBo(SysPostBo sysPostBo) {
        SysPost sysPost = ConvertUtils.convert(sysPostBo, SysPost.class);
        if (ObjectUtil.isNull(sysPost))
            throw new ServiceException("待校验对象为空");
        if (ObjectUtil.isEmpty(sysPost.getScope()))
            throw new ServiceException("作用域不能为空");
        List<ValidateBos> validateBosList = Stream.of(
                ValidateUtils.createNeValidateBos(ValidateUtils.createValidateBo(SysPost::getId, sysPost.getId(), "主键")),
                ValidateUtils.createOrEqValidateBos(
                        ValidateUtils.createValidateBo(SysPost::getNo, sysPost.getNo(), "编码"),
                        ValidateUtils.createValidateBo(SysPost::getCode, sysPost.getCode(), "标识"),
                        ValidateUtils.createValidateBo(SysPost::getName, sysPost.getName(), "名称"),
                        ValidateUtils.createValidateBo(SysPost::getRemark, sysPost.getRemark(), "备注"))
        ).collect(Collectors.toList());
        // 唯一校验
        ValidateUtils.uniqueValidate(this::countWrapper, validateBosList);
        if (StrUtil.isEmpty(sysPost.getId())) {
            // 新增
            if (Constants.TEN_SUPER_POST.equals(sysPost.getCode()))
                throw new ServiceException("标识不能为 " + Constants.TEN_SUPER_POST);
            if (Constants.SYS_SUPER_POST.equals(sysPost.getCode()))
                throw new ServiceException("标识不能为 " + Constants.SYS_SUPER_POST);
            sysPost.setNo(NoUtils.createNo(SecurityUtils.getTenantId(), "post"));
            sysPost.setTenantId(SecurityUtils.getUserInfo().getTenantId());
            return sysPost;
        } else {
            // 修改
            SysPost dbSysPost = getById(sysPost.getId());
            if (Constants.TEN_SUPER_POST.equals(sysPost.getCode()) && !sysPost.getCode().equals(dbSysPost.getCode()))
                throw new ServiceException("标识不能为 " + Constants.TEN_SUPER_POST);
            if (!dbSysPost.getEnabled().equals(sysPost.getEnabled()) && Constants.TEN_SUPER_POST.equals(sysPost.getCode()))
                throw new ServiceException("标识为 " + Constants.TEN_SUPER_POST + " 的岗位状态不能修改");
            if (Constants.SYS_SUPER_POST.equals(sysPost.getCode()) && !sysPost.getCode().equals(dbSysPost.getCode()))
                throw new ServiceException("标识不能为 " + Constants.SYS_SUPER_POST);
            if (!dbSysPost.getEnabled().equals(sysPost.getEnabled()) && Constants.SYS_SUPER_POST.equals(sysPost.getCode()))
                throw new ServiceException("标识为 " + Constants.SYS_SUPER_POST + " 的岗位状态不能修改");
            // 当修改了启用状态，并且是不启用得情况下
            if (!sysPost.getEnabled().equals(dbSysPost.getEnabled()) && !sysPost.getEnabled())
                // 如果被用户关联不能修改状态
                if (sysUserPostService.count(new LambdaQueryWrapper<SysUserPost>().in(SysUserPost::getPostId, sysPost.getId())) > 0)
                    throw new ServiceException(sysPost.getName() + "该岗位已被用户关联，不能修改状态为禁用");
            BeanUtil.copyProperties(sysPost, dbSysPost, true);
            return dbSysPost;
        }
    }

    /**
     * 删除前校验
     *
     * @param idList 主键列表
     * @return 主键列表
     */
    private List<String> delValid(List<String> idList) {
        if (ObjectUtil.isEmpty(idList))
            throw new ServiceException("待校验主键列表为空");
        List<SysPostBo> sysPostBoList = selectJoinList(SysPostBo.class, buildQueryWrapper(QuerySysPost.builder().idList(idList).build()));
        sysPostBoList.forEach(sysPostBo -> {
            if (Constants.TEN_SUPER_POST.equals(sysPostBo.getCode()))
                throw new ServiceException("标识为 " + Constants.TEN_SUPER_POST + " 的部门不能删除");
            if (Constants.SYS_SUPER_POST.equals(sysPostBo.getCode()))
                throw new ServiceException("标识为 " + Constants.SYS_SUPER_POST + " 的部门不能删除");
        });
        List<SysUserPost> sysUserPostList = sysUserPostService.list(new LambdaQueryWrapper<SysUserPost>().in(SysUserPost::getPostId, idList));
        sysPostBoList.forEach(sysPostBo -> sysPostBo.setSysUserPostList(sysUserPostList.stream().filter(sysUserPost -> sysPostBo.getId().equals(sysUserPost.getPostId())).collect(Collectors.toList())));
        sysPostBoList.forEach(sysPostBo -> {
            if (ObjectUtil.isNotEmpty(sysPostBo.getSysUserPostList()))
                throw new ServiceException(sysPostBo.getName() + "该岗位已被用户绑定不能删除");
        });
        return idList;
    }

    /**
     * 渲染视图
     *
     * @param sysPostVoList 视图列表
     * @return 渲染后的视图列表
     */
    private List<SysPostVo> renderSysPostVoList(List<SysPostVo> sysPostVoList) {
        if (ObjectUtil.isNotEmpty(sysPostVoList)) {
            // todo 扩展信息填入
            List<SysPostDept> sysPostDeptList = sysPostDeptService.list(new LambdaQueryWrapper<SysPostDept>().in(SysPostDept::getPostId, sysPostVoList.stream().map(SysPostVo::getId).collect(Collectors.toList())));
            sysPostVoList.forEach(sysPostVo -> sysPostVo.setDeptIdList(sysPostDeptList.stream().filter(sysPostDept -> sysPostVo.getId().equals(sysPostDept.getPostId())).map(SysPostDept::getDeptId).collect(Collectors.toList())));
        }
        return sysPostVoList;
    }

    /**
     * 构建查询条件
     *
     * @param querySysPost 查询用业务对象
     * @return 查询条件
     */
    private MPJLambdaWrapper<SysPost> buildQueryWrapper(QuerySysPost querySysPost) {
        MPJLambdaWrapper<SysPost> lambdaQueryWrapper = new MPJLambdaWrapper<SysPost>().selectAll(SysPost.class);
        lambdaQueryWrapper.eq(StrUtil.isNotEmpty(querySysPost.getId()), SysPost::getId, querySysPost.getId());
        lambdaQueryWrapper.in(ObjectUtil.isNotEmpty(querySysPost.getIdList()), SysPost::getId, querySysPost.getIdList());
        lambdaQueryWrapper.eq(StrUtil.isNotEmpty(querySysPost.getNo()), SysPost::getNo, querySysPost.getNo());
        lambdaQueryWrapper.like(StrUtil.isNotEmpty(querySysPost.getCode()), SysPost::getCode, querySysPost.getCode());
        lambdaQueryWrapper.like(StrUtil.isNotEmpty(querySysPost.getName()), SysPost::getName, querySysPost.getName());
        lambdaQueryWrapper.eq(StrUtil.isNotEmpty(querySysPost.getScope()), SysPost::getScope, querySysPost.getScope());
        lambdaQueryWrapper.eq(ObjectUtil.isNotEmpty(querySysPost.getSort()), SysPost::getSort, querySysPost.getSort());
        lambdaQueryWrapper.like(StrUtil.isNotEmpty(querySysPost.getRemark()), SysPost::getRemark, querySysPost.getRemark());
        lambdaQueryWrapper.eq(ObjectUtil.isNotEmpty(querySysPost.getEnabled()), SysPost::getEnabled, querySysPost.getEnabled());
        lambdaQueryWrapper.eq(StrUtil.isNotEmpty(querySysPost.getEnabledBy()), SysPost::getEnabledBy, querySysPost.getEnabledBy());
        lambdaQueryWrapper.eq(ObjectUtil.isNotEmpty(querySysPost.getEnabledTime()), SysPost::getEnabledTime, querySysPost.getEnabledTime());
        lambdaQueryWrapper.apply(StrUtil.isNotEmpty(querySysPost.getScope()), querySysPost.getScope());
        lambdaQueryWrapper.orderByAsc(SysPost::getSort);
        return lambdaQueryWrapper;
    }

}